package com.itheima.stock.service.impl;

import com.google.common.collect.Lists;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.entity.StockMarketIndexInfo;
import com.itheima.stock.pojo.entity.StockRtInfo;
import com.itheima.stock.pojo.vo.StockInfoConfig;
import com.itheima.stock.pojo.vo.StockMessage;
import com.itheima.stock.service.StockTimerTaskService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.utils.ParseType;
import com.itheima.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author : itheima
 * @date : 2023/8/31 10:44
 * @description :
 */
@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StockInfoConfig config;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    private HttpEntity<Map> httpEntity;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @PostConstruct//指定bean的初始化方法
//  @PreDestroy//指定bean的销毁方法
    public void initCommonHttpEntity(){
        HttpHeaders headers = new HttpHeaders();
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        this.httpEntity = new HttpEntity<>(headers);
    }

    @Override
    public void getInnerMarketInfo() {
        //1.组装请求url地址，格式：https://hq.sinajs.cn/list=sh000001,sz399001
        String url=config.getMarketUrl()+String.join(",",config.getInner());
        //2.组装httpEntity请求实体对象
//        HttpHeaders headers = new HttpHeaders();
//        headers.add("Referer","https://finance.sina.com.cn/stock/");
//        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//        HttpEntity<Map> httpEntity = new HttpEntity<>(headers);
        //3.发起请求
        ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //4.解析数据
        if (resp.getStatusCodeValue()!=200) {
            log.error("当前时间点：{},采集大盘数据失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
            return;
        }
        String jsData = resp.getBody();
        log.info("采集时间：{}，原始数据：{}",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),jsData);
        //基于java正则解析js数据
        String reg="var hq_str_(.+)=\"(.+)\";";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(jsData);
        ArrayList<StockMarketIndexInfo> infos = new ArrayList<>();
        while (matcher.find()) {
            //获取大盘的code
            String marketCode = matcher.group(1);
            //获取其它信息，字符串以逗号间隔
            String otherInfo=matcher.group(2);
            //以逗号切割字符串，形成数组
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName=splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint=new BigDecimal(splitArr[1]);
            //前收盘点
            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint=new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint=new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt=Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
            //时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();
            //封装对象
            StockMarketIndexInfo info = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .curPoint(curPoint)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeVolume(tradeVol)
                    .tradeAmount(tradeAmt)
                    .curTime(curTime)
                    .build();
            infos.add(info);
        }
        log.info("采集数据封装:{}",infos);
        //数据批量保存到数据库
        int count=stockMarketIndexInfoMapper.insertBatch(infos);
//        int count=infos.size();
        if (count != infos.size()) {
            log.error("数据插入异常，正常插入数据量：{},实际：{}，对应数据：{}",infos.size(),count,infos);
        }
        //通知backend工程刷新本地缓存
        StockMessage msg = new StockMessage();
        msg.setType(1);
        msg.setCurTime(new Date());//理想情况：当前时间发送消息后，backend接收后的时间差值不超过1分钟(越多越好)
        rabbitTemplate.convertAndSend("stockExchange","inner.market",msg);
    }

    @Override
    public void getStockRtIndex() {
        long startTime = System.currentTimeMillis();
        //1.获取所有股票编码
        List<String> codes = stockBusinessMapper.getAllCodes();
        //2.为股票添加业务前缀：sz sh
        List<String> prefixCodes = codes.stream().map(code -> code.startsWith("6") ? ("sh" + code) : ("sz" + code)).collect(Collectors.toList());
        //3.将股票编码集合分片，然后分批次请求三方接口获取原始js数据
        Lists.partition(prefixCodes,10).stream().forEach(stockCodes->{
//          new Thread(()->{
//              String url=config.getMarketUrl()+String.join(",",stockCodes);
//              //2.组装httpEntity请求实体对象
////            HttpHeaders headers = new HttpHeaders();
////            headers.add("Referer","https://finance.sina.com.cn/stock/");
////            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
////            HttpEntity<Map> httpEntity = new HttpEntity<>(headers);
//              //3.发起请求
//              ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//              if (resp.getStatusCodeValue()!=200) {
//                  log.error("当前时间：{},采集股票编码集合：{},出现异常",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),stockCodes);
//                  return;
//              }
//              String jsData = resp.getBody();
//              //4.解析js数据，封装实体对象
//              List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//              log.info("当前采集并封装的数据：{}",list);
//              //5.批量入库
////            int count=  stockRtInfoMapper.insertBatch(list);
//              int count=list.size();
//              if (count != list.size()) {
//                  log.error("数据插入异常，正常插入数据量：{},实际：{}，对应数据：{}",list.size(),count,list);
//              }
//          }).start();
            threadPoolTaskExecutor.execute(()->{
                String url=config.getMarketUrl()+String.join(",",stockCodes);
                //2.组装httpEntity请求实体对象
//            HttpHeaders headers = new HttpHeaders();
//            headers.add("Referer","https://finance.sina.com.cn/stock/");
//            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//            HttpEntity<Map> httpEntity = new HttpEntity<>(headers);
                //3.发起请求
                ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
                if (resp.getStatusCodeValue()!=200) {
                    log.error("当前时间：{},采集股票编码集合：{},出现异常",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),stockCodes);
                    return;
                }
                String jsData = resp.getBody();
                //4.解析js数据，封装实体对象
                List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                log.info("当前采集并封装的数据：{}",list);
                //5.批量入库
//            int count=  stockRtInfoMapper.insertBatch(list);
                int count=list.size();
                if (count != list.size()) {
                    log.error("数据插入异常，正常插入数据量：{},实际：{}，对应数据：{}",list.size(),count,list);
                }
            });
        });
        long diffTime=  System.currentTimeMillis()- startTime;
        log.info("采集股票数据耗时：{}",diffTime);
    }
}
